﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using Nature.Numerics;

namespace Nature
{	
	/// <summary>
    /// to be refined
    /// </summary>
	public interface IIdealGasSpeciesThermoPotentials
	{
		/// <summary>
	    /// Total number of chemical species
	    /// </summary>
		int Nsp { get; }
		 
		#region Cp - Methods
		/// <summary>
		/// Nondimensional ideal gas heat capacities at constant pressure [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: nondimensional ideal gas heat capacities at constant pressure [~]</param>
		void Cp(double temperature, double[] cp);

		/// <summary>
		/// Molar ideal gas heat capacities at constant pressure [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: molar ideal gas heat capacities at constant pressure [J/K/mol]</param>
		void MolarCp(double temperature, double[] cp);


		/// <summary>
		/// Specific ideal gas heat capacities at constant pressure [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: specific ideal gas heat capacities at constant pressure [J/K/kg]</param>
		void SpecificCp(double temperature, double[] cp);
		#endregion 
		 
		#region Cv - Methods
		/// <summary>
		/// Nondimensional ideal gas heat capacities at constant volume [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: nondimensional ideal gas heat capacities at constant volume [~]</param>
		void Cv(double temperature, double[] cv);

		/// <summary>
		/// Molar ideal gas heat capacities at constant volume [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: molar ideal gas heat capacities at constant volume [J/K/mol]</param>
		void MolarCv(double temperature, double[] cv);


		/// <summary>
		/// Specific ideal gas heat capacities at constant volume [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: specific ideal gas heat capacities at constant volume [J/K/kg]</param>
		void SpecificCv(double temperature, double[] cv);
		#endregion 
		 
		#region S - Methods
		/// <summary>
		/// Nondimensional ideal gas entropies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: nondimensional ideal gas entropies [~]</param>
		void S(double temperature, double[] s);

		/// <summary>
		/// Molar ideal gas entropies [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: molar ideal gas entropies [J/K/mol]</param>
		void MolarS(double temperature, double[] s);


		/// <summary>
		/// Specific ideal gas entropies [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: specific ideal gas entropies [J/K/kg]</param>
		void SpecificS(double temperature, double[] s);
		#endregion 
		 
		#region H - Methods
		/// <summary>
		/// Nondimensional ideal gas enthalpies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: nondimensional ideal gas enthalpies [~]</param>
		void H(double temperature, double[] h);

		/// <summary>
		/// Molar ideal gas enthalpies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: molar ideal gas enthalpies [J/mol]</param>
		void MolarH(double temperature, double[] h);


		/// <summary>
		/// Specific ideal gas enthalpies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: specific ideal gas enthalpies [J/kg]</param>
		void SpecificH(double temperature, double[] h);
		#endregion 
		 
		#region U - Methods
		/// <summary>
		/// Nondimensional ideal gas energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: nondimensional ideal gas energies [~]</param>
		void U(double temperature, double[] u);

		/// <summary>
		/// Molar ideal gas energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: molar ideal gas energies [J/mol]</param>
		void MolarU(double temperature, double[] u);


		/// <summary>
		/// Specific ideal gas energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: specific ideal gas energies [J/kg]</param>
		void SpecificU(double temperature, double[] u);
		#endregion 
		 
		#region G - Methods
		/// <summary>
		/// Nondimensional ideal gas Gibbs energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: nondimensional ideal gas Gibbs energies [~]</param>
		void G(double temperature, double[] g);

		/// <summary>
		/// Molar ideal gas Gibbs energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: molar ideal gas Gibbs energies [J/mol]</param>
		void MolarG(double temperature, double[] g);


		/// <summary>
		/// Specific ideal gas Gibbs energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: specific ideal gas Gibbs energies [J/kg]</param>
		void SpecificG(double temperature, double[] g);
		#endregion 
		 
		#region F - Methods
		/// <summary>
		/// Nondimensional ideal gas Helmholtz  energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: nondimensional ideal gas Helmholtz  energies [~]</param>
		void F(double temperature, double[] f);

		/// <summary>
		/// Molar ideal gas Helmholtz  energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: molar ideal gas Helmholtz  energies [J/mol]</param>
		void MolarF(double temperature, double[] f);


		/// <summary>
		/// Specific ideal gas Helmholtz  energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: specific ideal gas Helmholtz  energies [J/kg]</param>
		void SpecificF(double temperature, double[] f);
		#endregion 
		 
	}


	/// <summary>
    /// to be refined
    /// </summary>
	[Serializable]
	public abstract partial class IdealGasSpeciesThermoPotentialsBase : IIdealGasSpeciesThermoPotentials
	{
		private readonly double[] m_speciesMolarMasses;

		protected IdealGasSpeciesThermoPotentialsBase(IEnumerable<double> speciesMolarMasses)
		{
			m_speciesMolarMasses = speciesMolarMasses.ToArray();
		}

		public ReadOnlyArray<double> SpeciesMolarMasses { get { return m_speciesMolarMasses; } }

		public int Nsp { get { return m_speciesMolarMasses.Length; } }

		 
		#region Cp - Methods
		/// <summary>
		/// Nondimensional ideal gas heat capacities at constant pressure [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: nondimensional ideal gas heat capacities at constant pressure [~]</param>
		public abstract void Cp(double temperature, double[] cp);

		/// <summary>
		/// Molar ideal gas heat capacities at constant pressure [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: molar ideal gas heat capacities at constant pressure [J/K/mol]</param>
		public virtual void MolarCp(double temperature, double[] cp)
		{
			double factor = Constants.Rgas;
			this.Cp(temperature, cp);
			for(int isp = 0; isp < cp.Length; ++isp) { cp[isp] *= factor; }
		}


		/// <summary>
		/// Specific ideal gas heat capacities at constant pressure [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: specific ideal gas heat capacities at constant pressure [J/K/kg]</param>
		public virtual void SpecificCp(double temperature, double[] cp)
		{
			var w = m_speciesMolarMasses;
			double factor = Constants.Rgas;
			this.Cp(temperature, cp);
			for(int isp = 0; isp < cp.Length; ++isp) { cp[isp] *= factor / w[isp]; }
		}
		#endregion 
		 
		#region Cv - Methods
		/// <summary>
		/// Nondimensional ideal gas heat capacities at constant volume [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: nondimensional ideal gas heat capacities at constant volume [~]</param>
		public abstract void Cv(double temperature, double[] cv);

		/// <summary>
		/// Molar ideal gas heat capacities at constant volume [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: molar ideal gas heat capacities at constant volume [J/K/mol]</param>
		public virtual void MolarCv(double temperature, double[] cv)
		{
			double factor = Constants.Rgas;
			this.Cv(temperature, cv);
			for(int isp = 0; isp < cv.Length; ++isp) { cv[isp] *= factor; }
		}


		/// <summary>
		/// Specific ideal gas heat capacities at constant volume [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: specific ideal gas heat capacities at constant volume [J/K/kg]</param>
		public virtual void SpecificCv(double temperature, double[] cv)
		{
			var w = m_speciesMolarMasses;
			double factor = Constants.Rgas;
			this.Cv(temperature, cv);
			for(int isp = 0; isp < cv.Length; ++isp) { cv[isp] *= factor / w[isp]; }
		}
		#endregion 
		 
		#region S - Methods
		/// <summary>
		/// Nondimensional ideal gas entropies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: nondimensional ideal gas entropies [~]</param>
		public abstract void S(double temperature, double[] s);

		/// <summary>
		/// Molar ideal gas entropies [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: molar ideal gas entropies [J/K/mol]</param>
		public virtual void MolarS(double temperature, double[] s)
		{
			double factor = Constants.Rgas;
			this.S(temperature, s);
			for(int isp = 0; isp < s.Length; ++isp) { s[isp] *= factor; }
		}


		/// <summary>
		/// Specific ideal gas entropies [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: specific ideal gas entropies [J/K/kg]</param>
		public virtual void SpecificS(double temperature, double[] s)
		{
			var w = m_speciesMolarMasses;
			double factor = Constants.Rgas;
			this.S(temperature, s);
			for(int isp = 0; isp < s.Length; ++isp) { s[isp] *= factor / w[isp]; }
		}
		#endregion 
		 
		#region H - Methods
		/// <summary>
		/// Nondimensional ideal gas enthalpies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: nondimensional ideal gas enthalpies [~]</param>
		public abstract void H(double temperature, double[] h);

		/// <summary>
		/// Molar ideal gas enthalpies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: molar ideal gas enthalpies [J/mol]</param>
		public virtual void MolarH(double temperature, double[] h)
		{
			double factor = Constants.Rgas * temperature;
			this.H(temperature, h);
			for(int isp = 0; isp < h.Length; ++isp) { h[isp] *= factor; }
		}


		/// <summary>
		/// Specific ideal gas enthalpies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: specific ideal gas enthalpies [J/kg]</param>
		public virtual void SpecificH(double temperature, double[] h)
		{
			var w = m_speciesMolarMasses;
			double factor = Constants.Rgas * temperature;
			this.H(temperature, h);
			for(int isp = 0; isp < h.Length; ++isp) { h[isp] *= factor / w[isp]; }
		}
		#endregion 
		 
		#region U - Methods
		/// <summary>
		/// Nondimensional ideal gas energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: nondimensional ideal gas energies [~]</param>
		public abstract void U(double temperature, double[] u);

		/// <summary>
		/// Molar ideal gas energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: molar ideal gas energies [J/mol]</param>
		public virtual void MolarU(double temperature, double[] u)
		{
			double factor = Constants.Rgas * temperature;
			this.U(temperature, u);
			for(int isp = 0; isp < u.Length; ++isp) { u[isp] *= factor; }
		}


		/// <summary>
		/// Specific ideal gas energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: specific ideal gas energies [J/kg]</param>
		public virtual void SpecificU(double temperature, double[] u)
		{
			var w = m_speciesMolarMasses;
			double factor = Constants.Rgas * temperature;
			this.U(temperature, u);
			for(int isp = 0; isp < u.Length; ++isp) { u[isp] *= factor / w[isp]; }
		}
		#endregion 
		 
		#region G - Methods
		/// <summary>
		/// Nondimensional ideal gas Gibbs energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: nondimensional ideal gas Gibbs energies [~]</param>
		public abstract void G(double temperature, double[] g);

		/// <summary>
		/// Molar ideal gas Gibbs energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: molar ideal gas Gibbs energies [J/mol]</param>
		public virtual void MolarG(double temperature, double[] g)
		{
			double factor = Constants.Rgas * temperature;
			this.G(temperature, g);
			for(int isp = 0; isp < g.Length; ++isp) { g[isp] *= factor; }
		}


		/// <summary>
		/// Specific ideal gas Gibbs energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: specific ideal gas Gibbs energies [J/kg]</param>
		public virtual void SpecificG(double temperature, double[] g)
		{
			var w = m_speciesMolarMasses;
			double factor = Constants.Rgas * temperature;
			this.G(temperature, g);
			for(int isp = 0; isp < g.Length; ++isp) { g[isp] *= factor / w[isp]; }
		}
		#endregion 
		 
		#region F - Methods
		/// <summary>
		/// Nondimensional ideal gas Helmholtz  energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: nondimensional ideal gas Helmholtz  energies [~]</param>
		public abstract void F(double temperature, double[] f);

		/// <summary>
		/// Molar ideal gas Helmholtz  energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: molar ideal gas Helmholtz  energies [J/mol]</param>
		public virtual void MolarF(double temperature, double[] f)
		{
			double factor = Constants.Rgas * temperature;
			this.F(temperature, f);
			for(int isp = 0; isp < f.Length; ++isp) { f[isp] *= factor; }
		}


		/// <summary>
		/// Specific ideal gas Helmholtz  energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: specific ideal gas Helmholtz  energies [J/kg]</param>
		public virtual void SpecificF(double temperature, double[] f)
		{
			var w = m_speciesMolarMasses;
			double factor = Constants.Rgas * temperature;
			this.F(temperature, f);
			for(int isp = 0; isp < f.Length; ++isp) { f[isp] *= factor / w[isp]; }
		}
		#endregion 
		 
	}

	[Serializable]
	public partial class IdealGasSpeciesThermoPotentialsTabulated : IIdealGasSpeciesThermoPotentials
	{
		#region Private Fields
		private readonly IIdealGasSpeciesThermoPotentials m_potentials;
		 
		// --- Cp group:
		[NonSerialized]
		private Tabulated m_nondimCp = null;
		[NonSerialized]
		private Tabulated m_molarCp = null;
		[NonSerialized]
		private Tabulated m_specificCp = null;
		 
		// --- Cv group:
		[NonSerialized]
		private Tabulated m_nondimCv = null;
		[NonSerialized]
		private Tabulated m_molarCv = null;
		[NonSerialized]
		private Tabulated m_specificCv = null;
		 
		// --- S group:
		[NonSerialized]
		private Tabulated m_nondimS = null;
		[NonSerialized]
		private Tabulated m_molarS = null;
		[NonSerialized]
		private Tabulated m_specificS = null;
		 
		// --- H group:
		[NonSerialized]
		private Tabulated m_nondimH = null;
		[NonSerialized]
		private Tabulated m_molarH = null;
		[NonSerialized]
		private Tabulated m_specificH = null;
		 
		// --- U group:
		[NonSerialized]
		private Tabulated m_nondimU = null;
		[NonSerialized]
		private Tabulated m_molarU = null;
		[NonSerialized]
		private Tabulated m_specificU = null;
		 
		// --- G group:
		[NonSerialized]
		private Tabulated m_nondimG = null;
		[NonSerialized]
		private Tabulated m_molarG = null;
		[NonSerialized]
		private Tabulated m_specificG = null;
		 
		// --- F group:
		[NonSerialized]
		private Tabulated m_nondimF = null;
		[NonSerialized]
		private Tabulated m_molarF = null;
		[NonSerialized]
		private Tabulated m_specificF = null;
		 
		#endregion

		public IdealGasSpeciesThermoPotentialsTabulated(IIdealGasSpeciesThermoPotentials potentials, double tmin, double tmax, double delta)
		{
			if( potentials == null ){ throw new ArgumentNullException(); }
			if( potentials is IdealGasSpeciesThermoPotentialsTabulated) { throw new ArgumentException(); }
			m_potentials = potentials;
			Nsp = potentials.Nsp;		
			Tmin = tmin;
			Tmax = tmax;
			Delta = delta;
		}

		public int Nsp { get; private set; }

		public double Tmin { get; private set; }

		public double Tmax { get; private set; }

		public double Delta { get; private set; }

		 
		#region Cp - Methods
		/// <summary>
		/// Nondimensional ideal gas heat capacities at constant pressure [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: nondimensional ideal gas heat capacities at constant pressure [~]</param>
		public void Cp(double temperature, double[] cp)
		{
			if( m_nondimCp == null )
			{
				m_nondimCp = new Tabulated(
					(array, parameter) => m_potentials.Cp(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_nondimCp.Approximate(cp, temperature);
		}

		/// <summary>
		/// Molar ideal gas heat capacities at constant pressure [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: molar ideal gas heat capacities at constant pressure [J/K/mol]</param>
		public void MolarCp(double temperature, double[] cp)
		{
			if( m_molarCp == null )
			{
				m_molarCp = new Tabulated(
					(array, parameter) => m_potentials.MolarCp(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_molarCp.Approximate(cp, temperature);
		}


		/// <summary>
		/// Specific ideal gas heat capacities at constant pressure [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cp">Output: specific ideal gas heat capacities at constant pressure [J/K/kg]</param>
		public void SpecificCp(double temperature, double[] cp)
		{
			if( m_specificCp == null )
			{
				m_specificCp = new Tabulated(
					(array, parameter) => m_potentials.SpecificCp(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_specificCp.Approximate(cp, temperature);
		}
		#endregion 
		 
		#region Cv - Methods
		/// <summary>
		/// Nondimensional ideal gas heat capacities at constant volume [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: nondimensional ideal gas heat capacities at constant volume [~]</param>
		public void Cv(double temperature, double[] cv)
		{
			if( m_nondimCv == null )
			{
				m_nondimCv = new Tabulated(
					(array, parameter) => m_potentials.Cv(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_nondimCv.Approximate(cv, temperature);
		}

		/// <summary>
		/// Molar ideal gas heat capacities at constant volume [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: molar ideal gas heat capacities at constant volume [J/K/mol]</param>
		public void MolarCv(double temperature, double[] cv)
		{
			if( m_molarCv == null )
			{
				m_molarCv = new Tabulated(
					(array, parameter) => m_potentials.MolarCv(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_molarCv.Approximate(cv, temperature);
		}


		/// <summary>
		/// Specific ideal gas heat capacities at constant volume [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="cv">Output: specific ideal gas heat capacities at constant volume [J/K/kg]</param>
		public void SpecificCv(double temperature, double[] cv)
		{
			if( m_specificCv == null )
			{
				m_specificCv = new Tabulated(
					(array, parameter) => m_potentials.SpecificCv(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_specificCv.Approximate(cv, temperature);
		}
		#endregion 
		 
		#region S - Methods
		/// <summary>
		/// Nondimensional ideal gas entropies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: nondimensional ideal gas entropies [~]</param>
		public void S(double temperature, double[] s)
		{
			if( m_nondimS == null )
			{
				m_nondimS = new Tabulated(
					(array, parameter) => m_potentials.S(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_nondimS.Approximate(s, temperature);
		}

		/// <summary>
		/// Molar ideal gas entropies [J/K/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: molar ideal gas entropies [J/K/mol]</param>
		public void MolarS(double temperature, double[] s)
		{
			if( m_molarS == null )
			{
				m_molarS = new Tabulated(
					(array, parameter) => m_potentials.MolarS(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_molarS.Approximate(s, temperature);
		}


		/// <summary>
		/// Specific ideal gas entropies [J/K/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="s">Output: specific ideal gas entropies [J/K/kg]</param>
		public void SpecificS(double temperature, double[] s)
		{
			if( m_specificS == null )
			{
				m_specificS = new Tabulated(
					(array, parameter) => m_potentials.SpecificS(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_specificS.Approximate(s, temperature);
		}
		#endregion 
		 
		#region H - Methods
		/// <summary>
		/// Nondimensional ideal gas enthalpies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: nondimensional ideal gas enthalpies [~]</param>
		public void H(double temperature, double[] h)
		{
			if( m_nondimH == null )
			{
				m_nondimH = new Tabulated(
					(array, parameter) => m_potentials.H(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_nondimH.Approximate(h, temperature);
		}

		/// <summary>
		/// Molar ideal gas enthalpies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: molar ideal gas enthalpies [J/mol]</param>
		public void MolarH(double temperature, double[] h)
		{
			if( m_molarH == null )
			{
				m_molarH = new Tabulated(
					(array, parameter) => m_potentials.MolarH(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_molarH.Approximate(h, temperature);
		}


		/// <summary>
		/// Specific ideal gas enthalpies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="h">Output: specific ideal gas enthalpies [J/kg]</param>
		public void SpecificH(double temperature, double[] h)
		{
			if( m_specificH == null )
			{
				m_specificH = new Tabulated(
					(array, parameter) => m_potentials.SpecificH(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_specificH.Approximate(h, temperature);
		}
		#endregion 
		 
		#region U - Methods
		/// <summary>
		/// Nondimensional ideal gas energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: nondimensional ideal gas energies [~]</param>
		public void U(double temperature, double[] u)
		{
			if( m_nondimU == null )
			{
				m_nondimU = new Tabulated(
					(array, parameter) => m_potentials.U(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_nondimU.Approximate(u, temperature);
		}

		/// <summary>
		/// Molar ideal gas energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: molar ideal gas energies [J/mol]</param>
		public void MolarU(double temperature, double[] u)
		{
			if( m_molarU == null )
			{
				m_molarU = new Tabulated(
					(array, parameter) => m_potentials.MolarU(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_molarU.Approximate(u, temperature);
		}


		/// <summary>
		/// Specific ideal gas energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="u">Output: specific ideal gas energies [J/kg]</param>
		public void SpecificU(double temperature, double[] u)
		{
			if( m_specificU == null )
			{
				m_specificU = new Tabulated(
					(array, parameter) => m_potentials.SpecificU(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_specificU.Approximate(u, temperature);
		}
		#endregion 
		 
		#region G - Methods
		/// <summary>
		/// Nondimensional ideal gas Gibbs energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: nondimensional ideal gas Gibbs energies [~]</param>
		public void G(double temperature, double[] g)
		{
			if( m_nondimG == null )
			{
				m_nondimG = new Tabulated(
					(array, parameter) => m_potentials.G(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_nondimG.Approximate(g, temperature);
		}

		/// <summary>
		/// Molar ideal gas Gibbs energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: molar ideal gas Gibbs energies [J/mol]</param>
		public void MolarG(double temperature, double[] g)
		{
			if( m_molarG == null )
			{
				m_molarG = new Tabulated(
					(array, parameter) => m_potentials.MolarG(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_molarG.Approximate(g, temperature);
		}


		/// <summary>
		/// Specific ideal gas Gibbs energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="g">Output: specific ideal gas Gibbs energies [J/kg]</param>
		public void SpecificG(double temperature, double[] g)
		{
			if( m_specificG == null )
			{
				m_specificG = new Tabulated(
					(array, parameter) => m_potentials.SpecificG(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_specificG.Approximate(g, temperature);
		}
		#endregion 
		 
		#region F - Methods
		/// <summary>
		/// Nondimensional ideal gas Helmholtz  energies [~]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: nondimensional ideal gas Helmholtz  energies [~]</param>
		public void F(double temperature, double[] f)
		{
			if( m_nondimF == null )
			{
				m_nondimF = new Tabulated(
					(array, parameter) => m_potentials.F(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_nondimF.Approximate(f, temperature);
		}

		/// <summary>
		/// Molar ideal gas Helmholtz  energies [J/mol]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: molar ideal gas Helmholtz  energies [J/mol]</param>
		public void MolarF(double temperature, double[] f)
		{
			if( m_molarF == null )
			{
				m_molarF = new Tabulated(
					(array, parameter) => m_potentials.MolarF(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_molarF.Approximate(f, temperature);
		}


		/// <summary>
		/// Specific ideal gas Helmholtz  energies [J/kg]
		/// </summary>
		/// <param name="temperature">Input: gas mixture thermodynamic temperature [K]</param>
		/// <param name="f">Output: specific ideal gas Helmholtz  energies [J/kg]</param>
		public void SpecificF(double temperature, double[] f)
		{
			if( m_specificF == null )
			{
				m_specificF = new Tabulated(
					(array, parameter) => m_potentials.SpecificF(parameter, array), 
					Nsp, Tmin, Tmax, Delta);
			}
			m_specificF.Approximate(f, temperature);
		}
		#endregion 
		 
	} 

	public static partial class ITolerance
	{
		/// <summary>
        /// 
        /// </summary>
        /// <param name="potentials"></param>
        /// <param name="tmin"></param>
        /// <param name="tmax"></param>
        /// <param name="delta"></param>
        /// <returns></returns>
		public static IIdealGasSpeciesThermoPotentials Tabulate(this IIdealGasSpeciesThermoPotentials potentials, double tmin, double tmax, double delta)
		{
            var tabulated = potentials as IdealGasSpeciesThermoPotentialsTabulated;
            if (tabulated != null && tabulated.Tmin == tmin && tabulated.Tmax == tmax && tabulated.Delta == delta) 
            { 
                return tabulated; 
            }
			return new IdealGasSpeciesThermoPotentialsTabulated(potentials, tmin, tmax, delta);
		}

	}
}